/*=============================================================================
#     FileName: AacEncoder.cpp
#         Desc: 
#       Author: zhfayuan
#        Email: afst2012@outlook.com
#      Version: 0.0.1
#   LastChange: 2020-10-12 22:29:32
#      History:
=============================================================================*/
#include "AacEncoder.h"
#include <string.h>
#include <iomanip>


AacEncoder::AacEncoder()
{
    _aacBuffferLength = 1024;
    _aacBuffer = new uint8_t[_aacBuffferLength];
    _initialize = false;
    _handle = nullptr;
    _pcmBuffer = nullptr;
}

AacEncoder::~AacEncoder()
{
    if(_handle) {
        aacEncClose(&_handle);
        _handle = nullptr;
    }

    if(_pcmBuffer){
        delete _pcmBuffer;
        _pcmBuffer = nullptr;

    }
    _pcmBuffferLength = 0;

    if(_aacBuffer){
        delete _aacBuffer;
        _aacBuffer = nullptr;
    }
    _aacBuffferLength = 0;
}


void AacEncoder::initialize(uint32_t rate,uint32_t bit,uint32_t channel)
{

    uint32_t vbr = 0;
    uint32_t afterburner = 1;

    CHANNEL_MODE mode = MODE_1;
    AUDIO_OBJECT_TYPE profile=AOT_AAC_LC;//2
    TRANSPORT_TYPE payload=TT_MP4_ADTS;/*1:raw 1:adif 2:adts 6,7:latm 10:loas */

    if(_initialize)
        return;

    _aacEncRate = rate;
    _pcmAudioBit = bit;
    _pcmAudioChannel = channel;

    switch (channel)
    {
        case 1: mode = MODE_1;       break;
        case 2: mode = MODE_2;       break;
        case 3: mode = MODE_1_2;     break;
        case 4: mode = MODE_1_2_1;   break;
        case 5: mode = MODE_1_2_2;   break;
        case 6: mode = MODE_1_2_2_1; break;
    }

    aacEncOpen(&_handle,0,channel);
    aacEncoder_SetParam(_handle, AACENC_AOT,profile);
    aacEncoder_SetParam(_handle, AACENC_SAMPLERATE,rate);
    aacEncoder_SetParam(_handle, AACENC_CHANNELMODE, mode);
    aacEncoder_SetParam(_handle, AACENC_CHANNELORDER, 1);

    if(vbr) {
        aacEncoder_SetParam(_handle, AACENC_BITRATEMODE, vbr);
    } else {
        uint32_t bitrate = 44100;
        aacEncoder_SetParam(_handle, AACENC_BITRATE, bitrate);
    }

    aacEncoder_SetParam(_handle, AACENC_TRANSMUX,payload);
    aacEncoder_SetParam(_handle, AACENC_AFTERBURNER, afterburner);
    aacEncEncode(_handle, NULL, NULL, NULL, NULL);

    {
        AACENC_InfoStruct aacInfo;
        memset(&aacInfo,0,sizeof(aacInfo));
        aacEncInfo(_handle, &aacInfo);
        _pcmBuffferLength = channel* bit * aacInfo.frameLength >> 3;
        _pcmBuffer = new uint8_t[_pcmBuffferLength];
        _pcmBuffferedLength = 0;
    }

    //cout << "<AacEncoder> :"<< __FUNCTION__  <<",at:"<< __LINE__  << ",_pcmBuffferLength:"<< _pcmBuffferLength << ",_pcmBuffferedLength:" << _pcmBuffferedLength << endl;


    _timeMsPerSample = 1000.0f;
    _timeMsPerSample /= rate;

    _initialize = true;
}


void AacEncoder::encode(std::shared_ptr<std::vector<char>> &packet,uint32_t pkt_pts,std::function<void(std::shared_ptr<std::vector<char>>&,uint32_t)> func)
{
    int32_t insamples;
    int32_t outlen,len = 0,cached = 0,needed = 0;
    int32_t in_elem_size=2,out_elem_size=1;
    int32_t in_identifier,out_identifier;

    double tmpd;
    uint32_t pts_ms;

    char*outbuf,*inbuf;
    int32_t packet_size = packet->size();
    char * packet_data = packet->data();

    AACENC_ERROR err;
    AACENC_InArgs in_args;
    AACENC_OutArgs out_args;
    AACENC_BufDesc in_buf,out_buf;

    if( (packet_size <= 0)
    || (nullptr == func)) {
        return;
    }

    do
    {
        needed = _pcmBuffferLength - _pcmBuffferedLength;

        if(packet_size <= cached) {
            break;
        }

        len = packet_size - cached - needed;
        if( len >= 0) {

            memset(&in_args,0,sizeof(AACENC_InArgs));
            memset(&out_args,0,sizeof(AACENC_OutArgs));

            memset(&in_buf,0,sizeof(AACENC_BufDesc));
            memset(&out_buf,0,sizeof(AACENC_BufDesc));

            memcpy(_pcmBuffer+_pcmBuffferedLength,packet_data + cached,needed);
            _pcmBuffferedLength += needed;

            cached += needed;

            insamples = _pcmBuffferedLength /(_pcmAudioBit>>3);

            //IN
            in_elem_size = _pcmAudioBit>>3;
            in_identifier = IN_AUDIO_DATA;

            in_args.numInSamples = insamples;
            inbuf = (char*)_pcmBuffer;

            in_buf.numBufs = 1;
            in_buf.bufs = (void**)&inbuf;
            in_buf.bufferIdentifiers = &in_identifier;
            in_buf.bufSizes = (int*)&_pcmBuffferedLength;
            in_buf.bufElSizes = &in_elem_size;

            //OUT
            out_elem_size = 1;
            outbuf = (char*)_aacBuffer;
            outlen = _aacBuffferLength;
            out_identifier = OUT_BITSTREAM_DATA;


            out_buf.numBufs = 1;
            out_buf.bufs = (void**)&outbuf;
            out_buf.bufferIdentifiers = &out_identifier;
            out_buf.bufSizes = &outlen;
            out_buf.bufElSizes = &out_elem_size;


            if ((err = aacEncEncode(_handle,&in_buf,&out_buf,&in_args,&out_args)) != AACENC_OK) {
                goto NEXT;
            }

            if (out_args.numOutBytes <= 0){
                goto NEXT;
            }

            {
                std::shared_ptr<std::vector<char>> out;
                out = std::make_shared<std::vector<char>>();
                out->assign(_aacBuffer,_aacBuffer + out_args.numOutBytes);
                func(out,_timeMsOfAudio);
            }
NEXT:
            _pcmBuffferedLength =0;
        } else if( len < 0 ){

            if(0 == _pcmBuffferedLength ){

                insamples = _pcmAudioBit >> 3;
                tmpd = cached * _timeMsPerSample;
                tmpd /= _pcmAudioChannel;
                tmpd /= insamples;

                tmpd += pkt_pts;

                pts_ms = tmpd;

                _timeMsOfAudio = pts_ms;
            }

            memcpy(_pcmBuffer + _pcmBuffferedLength ,packet_data + cached,packet_size - cached);
            _pcmBuffferedLength += (packet_size - cached);
            cached = packet_size;
            break;
        }
    }while(1);
}


